﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Noris.Schedule.Support.Core
{
	//CLASS Combination PRO URČOVÁNÍ KOMBINACÍ HODNOT (matematická kombinace, permutace, variace) TYPU INT
	/// <summary>
	/// Třída pro generování permutací či kombinací hodnot typu int. Je třeba ji instancovat (nelze používat staticky).
	/// Nastavení typu kombinatoriky se provádí v konstruktoru.
	/// Aktuální kombinace hodnot je uložena v this.Values[].
	/// Hodnoty ve Values[] jsou 0 až (počet - 1), pokud je tedy třeba používat v aplikaci jiná data, 
	///   musí si aplikace připravit pole těchto dat a pak hodnoty this.Values[] používat v aplikačním kódu jako indexy pro tato externí data.
	/// Další postupnou kombinaci lze vložit do this.Values[] zavoláním metody Next().
	/// Znovu nastavit výchozí kombinaci lze metodou Reset(). Pak lze opět postupně číst všechny kombinace hodnot.
	/// Testování konce: this.End značí, že bylo požádáno (metodou Next()) o další kombinaci, a ta už neexistuje.
	/// this.Last značí, že v this.Values[] je právě nyní poslední kombinace hodnot.
	/// this.Correct značí, že zadaná data kombinací jsou platná (vztah count a items), a že kombinátor může vracet data.
	/// this.Number obsahuje číslo aktuální kombinace (0 = je připravena první, zvyšuje se o 1 každým posunem na další kombinaci).
	/// Kombinátor neprovádí předgenerování čísel, veškerá logika pro generování jedné kombinace je provedena v rámci metody Next.
	/// </summary>
	public class CombinatorCls
	{
		#region INSTANČNÍ PROMĚNNÉ, KONSTRUKTOR
		/// <summary>Typ kombinatoriky základní (Kombinace, Permutace)</summary>
		private CombinatorType _Type;
		/// <summary>Typ kombinatoriky, rozšířený o opakování (Kombinace s opakováním, Kombinace bez opakování, ...)</summary>
		private CombinatorRepeatingType _TypeRep;
		/// <summary>Počet vybíraných prvků</summary>
		private int _Items;
		/// <summary>Celkový počet prvků (vybírané hodnoty jsou v rozsahu 0 až (_Count-1))</summary>
		private int _Count;
		/// <summary>Pole posledně aktivních hodnot</summary>
		private int[] _Values;
		/// <summary>Příznak, že zadání je OK a může se generovat</summary>
		private bool _Correct;
		/// <summary>Příznak, že poslední volání Next skončilo "za poslední kombinací".</summary>
		private bool _End;
		/// <summary>Pořadí aktuální kombinace.</summary>
		private int _Number;
		/// <summary>Příznak, že zadání objektu je OK a může se používat</summary>
		public bool Correct { get { return this._Correct; } }
		/// <summary>Pole vygenerovaných hodnot. Je platné ihned po vytvoření objektu (pokud je objekt platný, což lze zjistit v this.Correct).</summary>
		public int[] Values;
		/// <summary>
		/// Příznak, že aktuální hodnoty (Value) jsou aplikací již použity.
		/// Na hodnotu false je nastavovávno v Initu a v metodě Next.
		/// Na hodnotu true i false může nastavit aplikace kdykoli potřebuje.</summary>
		public bool ValueIsUsed { get { return this._ValueIsUsed; } set { this._ValueIsUsed = value; } }
		private bool _ValueIsUsed;
		/// <summary>Příznak, že objekt právě nyní obsahuje poslední platnou kombinaci dat. Vyvolání Next v této situaci vrátí false.</summary>
		public bool Last { get { return this._IsLast(); } }
		/// <summary>Příznak, že kombinace už byly vyčerpány. Nastaví se na true poté, co bylo provedeno Next z poslední kombinace.</summary>
		public bool End { get { return this._End; } }
		/// <summary>Pořadí aktuální kombinace. -1 = neplatné zadání.</summary>
		public int Number { get { return this._Number; } }
		/// <summary>
		/// Prázdný konstruktor, vytvoří kombinátor který není inicializovaný.
		/// Inicializaci kombinátoru lze provést voláním metody Init().
		/// </summary>
		public CombinatorCls()
		{
			this._Fill((CombinatorRepeatingType)0, 0, 0);
		}
		/// <summary>
		/// Vytvoří objekt, který bude generovat permutace nebo kombinace s nebo bez opakování.
		/// </summary>
		/// <param name="type">Druh použitého algoritmu (viz enum Combinator.CombType)</param>
		/// <param name="items">
		/// Počet vybíraných prvků (v rozmezí 1 až count, jiný počet způsobí nefunkčnost objektu).
		/// Jde o množství prvků ve výstupní množině. Tolik prvků se vybírá z celkového počtu (count).
		/// </param>
		/// <param name="count">Celkový počet prvků, z nichž se vybírá. Pozor: jde o zero-base počet, tzn. při počtu count = 49 budou vybírána čísla 0-48 !</param>
		/// <param name="repetition">S opakováním</param>
		public CombinatorCls(CombinatorType type, int items, int count, bool repetition)
		{
			CombinatorRepeatingType type2 = _GetRepType(type, repetition);
			this._Fill(type2, items, count);
		}
		/// <summary>
		/// Vytvoří objekt, který bude generovat permutace nebo kombinace s nebo bez opakování.
		/// </summary>
		/// <param name="type">Druh použitého algoritmu (viz enum CombinatorRepeatingType)</param>
		/// <param name="items">
		/// Počet vybíraných prvků (v rozmezí 1 až count, jiný počet způsobí nefunkčnost objektu).
		/// Jde o množství prvků ve výstupní množině. Tolik prvků se vybírá z celkového počtu (count).
		/// </param>
		/// <param name="count">Celkový počet prvků, z nichž se vybírá. Pozor: jde o zero-base počet, tzn. při počtu count = 49 budou vybírána čísla 0-48 !</param>
		public CombinatorCls(CombinatorRepeatingType type, int items, int count)
		{
			this._Fill(type, items, count);
		}
		/// <summary>
		/// Inicializace kombinátoru.
		/// Pokud se kombinátor před inicializací nacházel v nějakém stavu, je tento stav zahozen.
		/// </summary>
		/// <param name="type">Druh použitého algoritmu (viz enum CombinatorRepeatingType)</param>
		/// <param name="items">
		/// Počet vybíraných prvků (v rozmezí 1 až count, jiný počet způsobí nefunkčnost objektu).
		/// Jde o množství prvků ve výstupní množině. Tolik prvků se vybírá z celkového počtu (count).
		/// </param>
		/// <param name="count">Celkový počet prvků, z nichž se vybírá. Pozor: jde o zero-base počet, tzn. při počtu count = 49 budou vybírána čísla 0-48 !</param>
		public void Init(CombinatorRepeatingType type, int items, int count)
		{
			this._Fill(type, items, count);
		}
		/// <summary>
		/// Inicializace kombinátoru.
		/// Pokud se kombinátor před inicializací nacházel v nějakém stavu, je tento stav zahozen.
		/// </summary>
		/// <param name="type">Druh použitého algoritmu (viz enum Combinator.CombType)</param>
		/// <param name="items">
		/// Počet vybíraných prvků (v rozmezí 1 až count, jiný počet způsobí nefunkčnost objektu).
		/// Jde o množství prvků ve výstupní množině. Tolik prvků se vybírá z celkového počtu (count).
		/// </param>
		/// <param name="count">Celkový počet prvků, z nichž se vybírá. Pozor: jde o zero-base počet, tzn. při počtu count = 49 budou vybírána čísla 0-48 !</param>
		/// <param name="repetition">S opakováním</param>
		public void Init(CombinatorType type, int items, int count, bool repetition)
		{
			CombinatorRepeatingType type2 = _GetRepType(type, repetition);
			this._Fill(type2, items, count);
		}
		/// <summary>
		/// Převede typ kombinátoru CombinatorType a příznak opakování repetition na typ CombinatorRepeatingType, který opakování obsahuje uvnitř.
		/// </summary>
		/// <param name="type"></param>
		/// <param name="repetition"></param>
		/// <returns></returns>
		private CombinatorRepeatingType _GetRepType(CombinatorType type, bool repetition)
		{
			switch (type)
			{	// Typ kombinace včetně opakování:
				case CombinatorType.Combination:
					return (repetition ? CombinatorRepeatingType.CombinationRep : CombinatorRepeatingType.CombinationNon);
				case CombinatorType.Permutation:
					return (repetition ? CombinatorRepeatingType.PermutationRep : CombinatorRepeatingType.PermutationNon);
			}
			return (CombinatorRepeatingType)0;
		}
		/// <summary>
		/// Vytvoří objekt, který bude generovat permutace nebo kombinace s nebo bez opakování.
		/// </summary>
		/// <param name="type">Druh použitého algoritmu (viz enum CombinatorRepeatingType)</param>
		/// <param name="items">
		/// Počet vybíraných prvků (v rozmezí 1 až count, jiný počet způsobí nefunkčnost objektu).
		/// Jde o množství prvků ve výstupní množině. Tolik prvků se vybírá z celkového počtu (count).
		/// </param>
		/// <param name="count">Celkový počet prvků, z nichž se vybírá. Pozor: jde o zero-base počet, tzn. při počtu count = 49 budou vybírána čísla 0-48 !</param>
		private void _Fill(CombinatorRepeatingType type, int items, int count)
		{
			this._Correct = false;
			if (this._IsCorrect(items, count))
			{
				this._Correct = true;
				this._Items = items;
				this._Count = count;
				this._TypeRep = type;
				this._Values = new int[items];
				this.Values = new int[items];
				this._SetFirst();
			}
			else
			{
				this._Values = new int[0];
				this.Values = new int[0];
				this._End = true;                   // Chybná definice = konec hned na začátku.
				this._Number = -1;
			}
			this._ValueIsUsed = false;              // Aplikační proměnná: tato kombinace dosud nebyla použita.
		}
		/// <summary>
		/// Zjistí, zda zadaná data jsou platná
		/// </summary>
		/// <param name="items">Počet vybíraných prvků</param>
		/// <param name="count">Celkový počet prvků, z nichž se vybírá.</param>
		/// <returns>true = data jsou OK</returns>
		private bool _IsCorrect(int items, int count)
		{
			return (count > 0 && items > 0 && items <= count);
		}
		public override string ToString()
		{
			string result = "";
			if (this._Correct)
			{
				switch (this._TypeRep)
				{
					case CombinatorRepeatingType.CombinationRep:
						result = "Kombinace s opakováním";
						break;
					case CombinatorRepeatingType.CombinationNon:
						result = "Kombinace bez opakování";
						break;
					case CombinatorRepeatingType.PermutationRep:
						result = "Permutace s opakováním";
						break;
					case CombinatorRepeatingType.PermutationNon:
						result = "Permutace bez opakování";
						break;
				}
				result += " {" + this._Items.ToString() + " z " + this._Count.ToString() + "}";
			}
			else
			{
				result = "Nesprávné zadání.";
			}
			return result;
		}
		#endregion
		#region PRIVÁTNÍ VÝKONNÉ METODY PRO GENEROVÁNÍ PRVNÍ / NÁSLEDUJÍCÍ KOMBINACE
		#region OBECNÉ HLAVIČKOVÉ METODY
		/// <summary>
		/// Podle typu kombinatoriky připraví do this._Values[] první kombinaci
		/// </summary>
		private void _SetFirst()
		{
			if (!this._Correct) return;              // Pokud není správně zadáno, pak je to konec.
			switch (this._TypeRep)
			{
				case CombinatorRepeatingType.CombinationRep:
					// Kombinace s opakováním (začíná se s hodnotami 0,0,0) :
					this._SetFirstCombRep();
					break;
				case CombinatorRepeatingType.CombinationNon:
					// Kombinace bez opakování (začíná se s hodnotami 0,1,2) :
					this._SetFirstCombNon();
					break;
				case CombinatorRepeatingType.PermutationRep:
					// Permutace s opakováním (začíná se s hodnotami 0,0,0) :
					this._SetFirstPermRep();
					break;
				case CombinatorRepeatingType.PermutationNon:
					// Permutace bez opakování (začíná se s hodnotami 0,1,2) :
					this._SetFirstPermNon();
					break;
				default:
					this._Correct = false;
					return;
					break;
			}
			this._ValueIsUsed = false;                // Aplikační proměnná: tato kombinace dosud nebyla použita.
			this._CopyValues();
			this._End = false;                        // Po začátku nejsme na konci.
			this._Number = 0;                         // Jsme na začátku, na první kombinaci.
		}
		/// <summary>
		/// Najde další kombinaci
		/// </summary>
		private void _SkipToNext()
		{
			if (!this._Correct) return;               // Pokud není správně zadáno, pak je to konec.

			switch (this._TypeRep)
			{
				case CombinatorRepeatingType.CombinationRep:
					// Kombinace s opakováním (začíná se s hodnotami 0,0,0) :
					this._SkipToNextCombRep();
					break;
				case CombinatorRepeatingType.CombinationNon:
					// Kombinace bez opakování (začíná se s hodnotami 0,1,2) :
					this._SkipToNextCombNon();
					break;
				case CombinatorRepeatingType.PermutationRep:
					// Permutace s opakováním (začíná se s hodnotami 0,0,0) :
					this._SkipToNextPermRep();
					break;
				case CombinatorRepeatingType.PermutationNon:
					// Permutace bez opakování (začíná se s hodnotami 0,1,2) :
					this._SkipToNextPermNon();
					break;
				default:
					this._Correct = false;
					return;
					break;
			}
			this._ValueIsUsed = false;                // Aplikační proměnná: tato kombinace dosud nebyla použita.
			this._CopyValues();
		}
		/// <summary>
		/// Zjistí, zda aktuálně uložená kombinace hodnot je poslední možná.
		/// </summary>
		/// <returns>true = skutečně je uložena poslední možná kombinace  /  false = ještě dokážeme vygenerovat další</returns>
		private bool _IsLast()
		{
			if (!this._Correct) return true;              // Pokud není správně zadáno, pak je to konec.
			if (this._End) return true;                   // Pokud jsme již narazili na konec, pak to víc nemusíme testovat.

			switch (this._TypeRep)
			{
				case CombinatorRepeatingType.CombinationRep:
					// Kombinace s opakováním (začíná se s hodnotami 0,0,0) :
					return this._IsLastCombRep();
				case CombinatorRepeatingType.CombinationNon:
					// Kombinace bez opakování (začíná se s hodnotami 0,1,2) :
					return this._IsLastCombNon();
				case CombinatorRepeatingType.PermutationRep:
					// Permutace s opakováním (začíná se s hodnotami 0,0,0, končí se s hodnotami 9,9,9)
					return this._IsLastPermRep();
				case CombinatorRepeatingType.PermutationNon:
					// Permutace dosud negeneruji, proto vracím true = konec je hned.
					return true;
					// Pro uvolnění je třeba naprogramovat metodu this.skipToNextPermNon()
					// Permutace bez opakování (začíná se s hodnotami 0,1,2) :
					return this._IsLastPermNon();
			}
			return true;
		}
		/// <summary>
		/// Přenese hodnoty z _Values[] do Values[]
		/// </summary>
		private void _CopyValues()
		{
			for (int v = 0; v < this._Items; v++)
				this.Values[v] = this._Values[v];
		}
		#endregion
		#region KOMBINACE S OPAKOVÁNÍM
		/// <summary>
		/// Nastaví první hodnotu _Values pro typ Kombinace s opakování
		/// </summary>
		private void _SetFirstCombRep()
		{
			// Výchozí stav je {0;0;0}:
			for (int v = 0; v < this._Items; v++)
				this._Values[v] = 0;
		}
		/// <summary>
		/// Přejde na další kombinaci s opakováním
		/// </summary>
		private void _SkipToNextCombRep()
		{
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// 1. Najít (zprava) pozici, na které se nachází první inkrementovatelná pozice,
			//       to je taková, která je nižší než nejvyšší hodnota (last).
			//     např. v kombinaci 179 (3 z 9) není možno inkrementovat  9, ale 7 (výsledek = 188)
			//     např. v kombinaci 199 (3 z 9) není možno inkrementovat 99, ale 1 (výsledek = 222)
			int inc = -1;
			for (int v = this._Items - 1; v >= 0; v--)    // Jdeme od konce, pořadí by mělo být 9 - 8 - 7 :
			{
				if (this._Values[v] < last)               // Pokud na pozici [v] je hodnota menší, než je strop této pozice:
				{
					inc = v;
					break;
				}
			}
			if (inc == -1) return;                        // Nelze inkrementovat, konec.

			// 2. Inkrement nalezené pozice, plus pozice doprava od ní :
			int val = this._Values[inc] + 1;              // Současná hodnota na pozici, kterou lze inkrementovat, plus 1
			for (int v = inc; v < this._Items; v++)
			{
				this._Values[v] = val;                    // Uložení (val) do pozice [v]
			}
			this._Number++;                               // Pořadové číslo další kombinace
		}
		/// <summary>
		/// Zjistí, zda nyní je v _Items uložena poslední dostupná kombinace hodnot pro typ Kombinace s opakování, tj. {9;9;9}
		/// </summary>
		/// <returns>true = jsme na konci  /  false = ne, ještě něco vyrobíme</returns>
		private bool _IsLastCombRep()
		{
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// Kombinace s opakováním: poslední stav je {9;9;9;} :
			for (int v = 0; v < this._Items; v++)
			{
				if (this._Values[v] != last)
					return false;
			}

			return true;
		}
		#endregion
		#region KOMBINACE BEZ OPAKOVÁNÍ
		/// <summary>
		/// Nastaví první hodnotu _Values pro typ Kombinace s bez opakování
		/// </summary>
		private void _SetFirstCombNon()
		{
			// Výchozí stav je {0;1;2}:
			for (int v = 0; v < this._Items; v++)
				this._Values[v] = v;
		}
		/// <summary>
		/// Přejde na další kombinaci bez opakování
		/// </summary>
		private void _SkipToNextCombNon()
		{
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// 1. Najít (zprava) pozici, na které se nachází první inkrementovatelná hodnota,
			//       to je taková, která je nižší než přípustná hodnota pro tuto pozici.
			//     např. v kombinaci 179 (3 z 9) není možno inkrementovat 9, ale 7 (výsledek = 189)
			//     např. v kombinaci 189 (3 z 9) není možno inkrementovat 8, ale 1 (výsledek = 234)
			int inc = -1;
			for (int v = this._Items - 1; v >= 0; v--)    // Jdeme od konce, pořadí by mělo být 9 - 8 - 7 :
			{
				if (this._Values[v] < last)               // Pokud na pozici [v] je hodnota menší, než je strop této pozice:
				{
					inc = v;
					break;
				}
				last--;                                   // Na další pozici (směrem doleva) by měla být hodnota o 1 menší.
			}
			if (inc == -1) return;                        // Nelze inkrementovat, konec.

			// 2. Inkrement nalezené pozice, plus pozice doprava od ní :
			int val = this._Values[inc];                  // Současná hodnota na pozici, kterou lze inkrementovat
			for (int v = inc; v < this._Items; v++)
			{
				this._Values[v] = ++val;                  // Inkrement (val), a pak uložení (val) do pozice [v]
			}
			this._Number++;                               // Pořadové číslo další kombinace
		}
		/// <summary>
		/// Zjistí, zda nyní je v _Items uložena poslední dostupná kombinace hodnot pro typ Kombinace bez opakování, tj. {7;8;9}
		/// </summary>
		/// <returns>true = jsme na konci  /  false = ne, ještě něco vyrobíme</returns>
		private bool _IsLastCombNon()
		{
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// Kombinace bez opakování: poslední stav je {7;8;9;} :
			for (int v = this._Items - 1; v >= 0; v--)    // Jdeme od konce, pořadí by mělo být 9 - 8 - 7 :
			{
				if (this._Values[v] != last)
					return false;
				last--;                                   // Na další pozici (směrem doleva) by měla být hodnota o 1 menší.
			}

			return true;
		}
		#endregion
		#region PERMUTACE S OPAKOVÁNÍM
		/// <summary>
		/// Nastaví první hodnotu _Values pro typ Permutace s opakováním
		/// </summary>
		private void _SetFirstPermRep()
		{
			// Výchozí stav je {0;0;0}:
			for (int v = 0; v < this._Items; v++)
				this._Values[v] = 0;
		}
		/// <summary>
		/// Přejde na další permutaci s opakováním
		/// </summary>
		private void _SkipToNextPermRep()
		{
			// Postupné stavy jsou : {0;0;0}; {0;0;1}; {0;0;2}; {0;0;3}; ...; {0;1;0}; {0;1;1}; {0;1;2}; {0;1;3}; ...; {0;2;0}; ...; {3;3;2}; {3;3;3}:
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// 1. Najít (zprava) pozici, na které se nachází první inkrementovatelná hodnota,
			//       to je taková, která je nižší než poslední hodnota
			//     např. v kombinaci 179 (3 z 9) není možno inkrementovat 9, ale 7 (výsledek = 180)
			//     např. v kombinaci 199 (3 z 9) není možno inkrementovat 99, ale 1 (výsledek = 200)
			int inc = -1;
			for (int v = this._Items - 1; v >= 0; v--)    // Jdeme od konce
			{
				if (this._Values[v] < last)               // Pokud na pozici [v] je hodnota menší, než je strop této pozice:
				{
					inc = v;
					break;
				}
			}
			if (inc == -1) return;                        // Nelze inkrementovat, konec.

			// 2. Inkrement nalezené pozice, plus vložit nulu na pozice doprava od ní :
			int val = this._Values[inc] + 1;              // Nová hodnota na pozici, kterou lze inkrementovat
			for (int v = inc; v < this._Items; v++)
			{
				this._Values[v] = val;                    // Vložit hodnotu na danou pozici
				val = 0;                                  // a protože budeme pokračovat na další pozice doprava, budeme tam vkládat nulu.
			}
			this._Number++;                               // Pořadové číslo další kombinace
		}
		/// <summary>
		/// Zjistí, zda nyní je v _Items uložena poslední dostupná kombinace hodnot pro typ Permutace s opakováním, tj. {9;9;9}
		/// </summary>
		/// <returns>true = jsme na konci  /  false = ne, ještě něco vyrobíme</returns>
		private bool _IsLastPermRep()
		{
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// Kombinace s opakováním: poslední stav je {9;9;9;} :
			for (int v = 0; v < this._Items; v++)
			{
				if (this._Values[v] != last)
					return false;
			}
			return true;
		}
		#endregion
		#region PERMUTACE BEZ OPAKOVÁNÍ
		/// <summary>
		/// Nastaví první hodnotu _Values pro typ Permutace bez opakování
		/// </summary>
		private void _SetFirstPermNon()
		{
			// Výchozí stav je {0;1;2}:
			for (int v = 0; v < this._Items; v++)
				this._Values[v] = v;
		}
		/// <summary>
		/// Přejde na další permutaci bez opakování
		/// </summary>
		private void _SkipToNextPermNon()
		{
			if (_IsLastPermNon()) return;                 // pojistka

			// Postupné stavy jsou : {0;1;2}; {0;1;3}; {0;1;4}; ...; {0;2;1}; {0;2;3}; {0;2;4}; {0;3;1}; {0;3;2}; {0;3;4} ...; {4;0;1}; ...; {4;3;1}; {4;3;2}:
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// Nechce se mi vymýšlet komplikovaný postup, když se nabízí jednoduché řešení:
			while (true)
			{
				// 1. Zavolám metodu pro další určení permutace s opakováním:
				_SkipToNextPermRep();
				if (_IsLastPermNon()) break;

				// 2. Prověřím, zda v aktuálních datech je přítomné opakování:
				bool rep = _ContainRepeatedValues();

				// 3. Pokud není opakování, skončím, jinak vygeneruji další permutaci (s opakováním) a znova test...:
				if (!rep) break;
			}
		}
		/// <summary>
		/// Zjistí, zda současná sada hodnot this._Values obsahuje opakování některé hodnoty.
		/// Vrací true = je tam opakování / false = je to bez opakování.
		/// </summary>
		/// <returns></returns>
		private bool _ContainRepeatedValues()
		{
			System.Collections.Hashtable ht = new System.Collections.Hashtable();
			for (int v = 0; v < this._Items; v++)
			{
				int val = this._Values[v];
				if (ht.ContainsKey(val)) return true;
				ht.Add(val, null);
			}
			return false;
		}
		/// <summary>
		/// Zjistí, zda nyní je v _Items uložena poslední dostupná kombinace hodnot typ Permutace bez opakování, tj. {9;8;7}
		/// </summary>
		/// <returns>true = jsme na konci  /  false = ne, ještě něco vyrobíme</returns>
		private bool _IsLastPermNon()
		{
			int last = this._Count - 1;                   // Poslední číslice (v komentářích je uváděno jako 9)

			// Permutace bez opakování: poslední stav je {9;8;7} :
			for (int v = 0; v < this._Items; v++)         // Jdeme od počátku, pořadí by mělo být 9 - 8 - 7 :
			{
				if (this._Values[v] != last)
					return false;
				last--;                                   // Na další pozici (směrem doprava) by měla být hodnota o 1 menší.
			}
			return true;
		}
		#endregion
		#endregion
		#region VEŘEJNÉ METODY
		/// <summary>
		/// Znovu se nastaví na počátek soupisu kombinací.
		/// Poté by měla hodnota End být false, hodnoty Values[] budou iniciované a bude možno procházet skrz kombinace pomocí metody Next().
		/// </summary>
		public void Reset()
		{
			if (!this._Correct) return;
			this._SetFirst();                     // Iniciuje Values[] a nastaví _End = false;
		}
		/// <summary>
		/// Vygeneruje další kombinaci.
		/// Pokud další kombinace není, vrací false.
		/// Tato metoda by se měla používat na konci výkonné smyčky, posouvá hodnoty Values na další.
		/// Po vytvoření tohoto objektu (a po provedení metody Reset()) objekt obsahuje první kombinaci,
		/// po prvním Next() obsahuje druhou (a pak další), a po posledním Next(), který vrátí false, už by se data neměla číst (obsahují stále poslední kombinaci).
		/// Poté, kdy Next vrací false (další kombinace už není) se nastaví End na true, to je možno testovat např. ve smyčce while (!End).
		/// </summary>
		/// <remarks>
		/// Dva druhy používání:
		/// A) instance objektu; while (true) { práce s .Values; if (!Next()) break; }      // Na konci smyčky se testuje výsledek Next()
		/// B) instance objektu; while (!End) { práce s .Values; Next(); }                  // Na počátku smyčky se testuje, zda už nejsme za poslední kombinací hodnot
		/// </remarks>
		/// <returns></returns>
		public bool Next()
		{
			if (this._End) return false;         // Pokud jsme za koncem, nic neřešíme a vracíme false.
			if (this._IsLast())
			{	// Pokud stojíme na konci kombinací (na poslední kombinaci), nahodíme: _End = true a vrátíme false.
				this._End = true;
				return false;
			}

			this._SkipToNext();

			return true;
		}
		#endregion
		#region ENUM CombinatorType = TYPY KOMBINATORIK VEŘEJNÉ A CombinatorRepeatingType = TYPY VČETNĚ OPAKOVÁNÍ
		/// <summary>
		/// Druh použitého algoritmu
		/// </summary>
		public enum CombinatorType
		{
			/// <summary>
			/// Kombinace: nezáleží na pořadí prvků. Výsledná množina {1;3;5} je tedy shodná, jako výsledná množina {3;5;1}.
			/// Bude tedy vrácena množina {1;2;3}, ale pak už nebude vrácena množina {3;2;1} 
			/// (protože ta již byla vrácena v podobě {1;2;3}).
			/// Existuje kombinace s opakováním (tj. může být vrácena množina {1;1;1})
			/// anebo bez opakování, pak nebude vrácena množina {1;1;1}, ale {1;2;3}.
			/// </summary>
			Combination,
			/// <summary>
			/// Permutace: záleží na pořadí prvků. Výsledná množina {1;3;5} je tedy jiná, než výsledná množina {3;5;1}.
			/// Postupně tedy budou vráceny množiny {1;2;3} i {3;2;1}.
			/// Existuje permutace s opakováním (tj. může být vrácena množina {1;1;1})
			/// anebo bez opakování, pak nebude vrácena množina {1;1;1}, ale {1;2;3}.
			/// </summary>
			Permutation
		}
		/// <summary>
		/// Druh algoritmu se zohledněním opakování (pro rychlé větvení podle typu a opakování)
		/// </summary>
		public enum CombinatorRepeatingType
		{
			CombinationRep = 1,
			CombinationNon,
			PermutationRep,
			PermutationNon
		}
		#endregion
	}
}
